;;;
;;; solution2.lisp: Solution for selected exercises in LISP tutorial 2
;;; Philip W. L. Fong
;;; SFU CMPT 310 (2001-1)
;;;

;;
;; Tail Recursions
;;

(defun fast-triangular (N)
  "Compute the N'th triangular number."
  (fast-triangular-aux N 1))

(defun fast-triangular-aux (N A)
  "Compute the product of A and the N'th triangular number."
  (if (= N 1)
      A
    (fast-triangular-aux (1- N) (+ N A))))

(defun fast-power (B E)
  "Raise B to the E'th power."
  (fast-power-aux B E 1))

(defun fast-power-aux (B E A)
  "Compute the product of A and the E'th power of B."
  (if (zerop E)
      A
    (fast-power-aux B (1- E) (* B A))))

(defun fast-list-length (L)
  "Compute the length of list L."
  (fast-list-length-aux L 0))

(defun fast-list-length-aux (L A)
  "Compute the sum of A and the length of list L."
  (if (null L)
      A
    (fast-list-length-aux (rest L) (1+ A))))

;;
;; Lambda Expressions
;;

(defun apply-func-list (L X)
  "Apply a list L of functions to object X."
  (if (null L)
      X
    (funcall (first L) (apply-func-list (rest L) X))))

;; 10 times the fourth element of the list (10 20 30 40 50)
(apply-func-list (list #'(lambda (N) (* 10 N)) #'fourth) '(10 20 30 40 50))

;; the third element of the second element in the list 
;; ((1 2) (3 4 5) (6))
(apply-func-list (list #'third #'second) '((1 2) (3 4 5) (6)))

;; the difference between 10 and the length of (a b c d e f)
(apply-func-list (list #'(lambda (N) (- 10 N)) #'list-length) '(a b c d e f))

;; a list containing a list containing the symbol 'blah
(apply-func-list (list #'list #'list) 'blah)

;;
;; Search Iteration
;;

(defun find-nonempty (L)
  "Given a list L of lists, return the leftmost nonempty member."
  (if (null L)
      nil
    (if (not (null (first L)))
	(first L)
      (find-nonempty (rest L)))))

(defun find-long-list (L)
  "Given a list L of lists, return the leftmost member with length at least 3."
  (find-if #'(lambda (X) (>= (list-length X) 3)) L))

(defun find-even-list (L)
  "Given a list L of lists, return the leftmost member with even length."
  (find-if #'(lambda (X) (evenp (list-length X))) L))

(defun find-divisible-by-three (L)
  "Given a list L of numbers, return the leftmost member that is divisible by 3."
  (find-if #'(lambda (X) (zerop (rem X 3))) L))

;;
;; Filter Iteration
;;

(defun list-remove-if (P L)
  "Return a list containing all elements of list L except for those satisfying predicate P."
  (if (null L)
      nil
    (if (funcall P (first L))
	(list-remove-if P (rest L))
      (cons (first L) (list-remove-if P (rest L))))))

(defun list-difference (L1 L2)
  "Compute the difference of lists L1 and L2."
  (remove-if #'(lambda (X) (member X L2)) L1))

;;
;; Functions Returning Multiple Values
;;

(defun list-min-max (L)
  "Given a list L of numbers, return two values, the minimum and the maximum members of L."
  (if (null L)
      (values nil nil)
    (list-min-max-aux (rest L) (first L) (first L))))

(defun list-min-max-aux (L MINIMUM MAXIMUM)
  "Given a list L of numbers, return (min LMIN MINIMUM) and (max LMAX MAXIMUM), where LMIN and LMAX are the minimum and maximum members of L respectively."
  (if (null L)
      (values MINIMUM MAXIMUM)
    (list-min-max-aux (rest L) 
		      (min (first L) MINIMUM) 
		      (max (first L) MAXIMUM))))

